﻿using AutoMapper;
using Microsoft.EntityFrameworkCore;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Volo.Abp;
using Volo.Abp.Application.Services;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Repositories;
using Volo.Abp.Linq;
using Volo.Abp.Users;
using YaAppoint.Appointments.Pools;
using YaAppoint.CustomSettings;
using YaAppoint.EntityFrameworkCore;
using YaAppoint.OnlineServiceCache;
using YaAppoint.Response;
using YaAppoint.TextTemplates;

namespace YaAppoint.Appointments.CheckIn
{
    /// <summary>
    /// 签到服务
    /// </summary>
    [RemoteService(isEnabled: false)]
    public class CheckInAppService(IRepository<Appointment,Guid> appointmentRepository,
                                   IRepository<TextTemplate,Guid> textTemplateRepository,
                                   IRepository<ExamApplication,Guid> examApplicationRepository,
                                   IMapper mapper,
                                   IOnlineServiceCacheAppService onlineServiceCacheService,
                                   ICustomSettingAppService customSettingAppService,
                                   IRepository<Department,Guid> departmentRepository,
                                   IRepository<Pool,Guid> poolRepository,
                                   AsyncQueryableExecuter queryableExecuter,
                                   YaAppointDbContext dbContext) : ApplicationService, ICheckInAppService, ITransientDependency
    {
        private readonly IRepository<Appointment, Guid> _appointmentRepository = appointmentRepository;
        private readonly IRepository<TextTemplate, Guid> _textTemplateRepository = textTemplateRepository;
        private readonly IRepository<Department, Guid> _departmentRepository = departmentRepository;
        private readonly IRepository<ExamApplication, Guid> _examApplicationRepository = examApplicationRepository;
        private readonly ICustomSettingAppService _customSettingAppService = customSettingAppService;
        private readonly AsyncQueryableExecuter _queryableExecuter= queryableExecuter;
        private readonly YaAppointDbContext _dbContext = dbContext;
        private readonly IRepository<Pool, Guid> _poolRepository = poolRepository;
        private readonly IOnlineServiceCacheAppService _onlineServiceCacheService= onlineServiceCacheService;
        private readonly IMapper _mapper = mapper;


        /// <summary>
        /// 签到
        /// </summary>
        /// <param name="appointmentIds"></param>
        /// <returns></returns>
        public async Task<RomoteCheckInRes> CheckInAsync(string appointNumber)
        {
            var onlineService = await _textTemplateRepository.FirstOrDefaultAsync(t => t.Type == TemplateType.Service && t.Interface != null && t.Interface.Equals("ICheckInByApi") && t.IsActive);
            BackJson res = new BackJson();
            RomoteCheckInRes romoteRes;
            // 动态编译(使用api)
            if (onlineService != null)
            {
                // 从缓存中获取实例
                var cachedService = await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate, TextTemplateDto>(onlineService));
                if (cachedService.GetInstance() is ICheckInByApi service)
                {
                   return  await service.CheckInAsync(appointNumber); // 返回远程签到结果
                }
                else
                {
                    throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                    {
                        Data = {
                            { "templateName",onlineService.Name},
                            { "interface",onlineService.Interface}
                        }
                    };
                }
            }
            else
            {
                throw new BusinessException(CustomErrorCodes.TextTemplateNotExist);
            }
        }


        /// <summary>
        /// 自动签到
        /// </summary>
        public async Task AutoCheckIn()
        {
            var depList = await _departmentRepository.GetListAsync();
            List<string> appointNumberList = new List<string>();
            List<Appointment> toUpdateEntities = new List<Appointment>();
            foreach (var dep in depList) // 遍历科室
            {
                var deviceList = dep.Devices;
                foreach (var device in deviceList) // 遍历科室设备
                {
                    // 获取设备自定义设置
                    // 获取设备自定义设置
                    var setting = await _customSettingAppService.GetDeviceSetting(device.Id);
                    double earliest = (double)setting.Earliest!;
                    var pools = await _poolRepository.GetListAsync(p=>p.DeviceId==device.Id //需要自动签到的设备
                                                                      && p.StartTime.AddMinutes(-earliest)<=DateTime.Now // 到达自动签到时间
                                                                      && p.EndTime>DateTime.Now  // 号池没结束
                                                                      && p.Appointments!=null    // 号池有预约
                                                                      && p.Appointments.Count>0);// 当前设备下可以自动签到的号池
                    foreach (var pool in pools)  // 遍历可自动签到的号池
                    {
                        var appointments = pool.Appointments!.AsQueryable().Where(a => a.Status == AppointmentStatus.Submit).ToList();
                        foreach (var appointment in appointments)
                        {
                            appointment.Status = AppointmentStatus.Achieve;
                            toUpdateEntities.Add(appointment);
                            appointNumberList.Add(appointment.Number); // 添加到需要签到的预约ID集合中
                        }
                    }
                }
            }
            await _appointmentRepository.UpdateManyAsync(toUpdateEntities);

            // 如果有在线服务则调用在线服务
            var onlineService = await _textTemplateRepository.FirstOrDefaultAsync(t => t.Type == TemplateType.Service && t.Interface != null && t.Interface.Equals("ICheckInByApi") && t.IsActive);
            if (onlineService != null)
            {
                // 从缓存获取
                var cachedService =await _onlineServiceCacheService.GetOrAddOnlineService(_mapper.Map<TextTemplate,TextTemplateDto>(onlineService));
                if (cachedService.GetInstance() is ICheckInByApi service)
                {
                    await service.BatchCheckInAsync(appointNumberList);
                }
                else
                {
                    throw new BusinessException(CustomErrorCodes.TemplateInterfaceUnimplemented)
                    {
                        Data = {
                            { "templateName",onlineService.Name},
                            { "interface",onlineService.Interface}
                        }
                    };
                }
            }
        }

        /// <summary>
        /// PACS内签到成功后回写
        /// </summary>
        /// <param name="appointmentIds"></param>
        public async Task<BackJson> WriteBackAsync(List<string> appointNumbers)
        {
            if (appointNumbers != null && appointNumbers.Count > 0)
            {
                var list = await _appointmentRepository.GetListAsync(a => appointNumbers.Contains(a.Number));
                foreach (var appoint in list)
                {
                    appoint.Status = AppointmentStatus.Achieve;
                }
                return new BackJson()
                {
                    code = 0,
                    msg = "回写成功。"
                };
            }
            else
            {
                return new BackJson()
                {
                    code=1,
                    msg= "appointmentIds参数为空或元素数量为0"
                };
            }
        }

        /// <summary>
        /// 向PACS推送检查项目的预约信息
        /// </summary>
        /// <param name="appointNumbers">预约编号集合</param>
        /// <returns></returns>
        public async Task<BackJson> PushApplyItemAppointmentData(List<string> appointNumbers)
        {
            var list =await  _dbContext.PacsExamApplyItems.Where(p => appointNumbers.Contains(p.AppointNumber))
                                                    .OrderBy(p => p.AppointNumber)
                                                    .ToListAsync();
            var data =new Dictionary<string, object>();
            foreach (var appointNumber in appointNumbers)
            {
                var subList = list.Where(e => e.AppointNumber == appointNumber);
                data.Add(appointNumber,subList);
            }

            return new BackJson()
            {
                code = 0,
                data = data
            };
        }

        /// <summary>
        /// 获取预约列表
        /// </summary>
        /// <param name="number"></param>
        /// <param name="userData"></param>
        /// <returns></returns>
        public async Task<BackJson> GetAppointmentList(string? number, Dictionary<string, object> userData)
        {
            var query = await _examApplicationRepository.GetQueryableAsync();
            query = query.Where(e => e.AppointmentId != null);
           
            if (!string.IsNullOrWhiteSpace(number))
            {
                query = query.Where(e =>e.Appointment.Number.Contains(number));
            }
            for (int i = 0; i < userData.Count; i++)
            {
                var item = userData.ElementAt(i);

                if (item.Key.Equals("timeRange"))
                {
                    var value = JsonConvert.DeserializeObject<List<DateTime>>(userData["timeRange"].ToString());
                    if (value != null && value.Count > 0)
                    {
                        query = query.Where(ea => ea.Appointment!.Pool.StartTime >= value[0] && ea.Appointment.Pool.EndTime <= value[1]);
                    }
                }

                if (item.Key.Equals("medicalCardNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(e => e.MedicalCardNumber!.Equals(value));
                    }
                }

                if (item.Key.Equals("name"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(a => a.Name.Contains(value));
                    }
                }

                if (item.Key.Equals("outPatientNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(a => a.OutPatientNumber.Equals(value));
                    }
                }

                if (item.Key.Equals("inPatientNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(e => e.InPatientNumber.Equals(value));
                    }
                }

                if (item.Key.Equals("physicalExamNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(e => e.PhysicalExamNumber.Equals(value));
                    }
                }

                if (item.Key.Equals("phoneNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(e => e.PhoneNumber.Contains(value));
                    }
                }

                if (item.Key.Equals("idCardNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrEmpty(value))
                    {
                        query = query.Where(e => e.IdCardNumber.Equals(value));
                    }
                }

                if (item.Key.Equals("medicalInsuranceNumber"))
                {
                    var value = item.Value as string;
                    if (!string.IsNullOrWhiteSpace(value))
                    {
                        query = query.Where(e => e.MedicalInsuranceNumber.Equals(value));
                    }
                }
            }
            var depId = CurrentUser.FindClaimValue("department-id");
            int earliest = 60;
            if (!string.IsNullOrEmpty(depId))
            {
                // 当前科室
                var dep = await _departmentRepository.FirstOrDefaultAsync(d => d.Id == Guid.Parse(depId));
                if (dep != null)
                {
                    // 预约到当前科室的设备下的
                    query = query.Where(e =>dep.Devices.Contains(e.Appointment.Pool.Device));
                }
            }
            //query = query.Where(e => e.Appointment.Pool.StartTime.AddMinutes(0 - earliest) <= DateTime.Now);
            query = query.OrderBy(e => e.Appointment.Status).ThenBy(e=>e.Appointment.Pool.StartTime);
            var list = await _queryableExecuter.ToListAsync(query);
            var appointList = list.DistinctBy(e => e.AppointmentId).ToList();

            var data = appointList.Select(e => new
            {
                id = e.AppointmentId,
                number = e.Appointment!.Number,
                name = e.Name,
                phoneNumber = e.PhoneNumber,
                idCard =e.IdCardNumber,
                displayName = e.Appointment.DisplayName,
                status = e.Appointment.Status,
                isActive = e.Appointment.IsActive,
                examApplies = e.Appointment.ExamApplications.Select(ea => new
                {
                    examItemName = ea.ExamItemName,
                    examItemCode = ea.ExamItemCode,
                    name = ea.Name,
                    examBodyPart = ea.ExamBodyPart,
                    examItemFee = ea.ExamItemFee
                })
            });

            return new BackJson
            {
                code =0,
                data = data
            };
        }
    }
}
